/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "aki/jsbind.h"
#include "aki/value.h"
#include "napi/native_api.h"
#include <iostream>

template <typename K, typename T>
void printMap(std::map<K, T> m)
{
    for (auto& [key, value] : m) {
        AKI_LOG(INFO) << key << " : " << value;
    }
}

class Person {
public:
    int age;
    std::string name;
    Person() = default;
    Person(int age, std::string name) : age(age), name(name) {}
    bool operator < (const Person& other) const
    {
        return name < other.name;
    }
};

void printArrayBuffer(const aki::ArrayBuffer& arrayBuffer)
{
    // 使用 const_cast 去掉 const 限制
    AKI_LOG(INFO) << const_cast<aki::ArrayBuffer&>(arrayBuffer).GetData();
}

template<typename T>
void printVector(const std::vector<T>& v)
{
    for (const T& _v : v) {
        AKI_LOG(INFO) << _v << " : ";
    }
}

void printPerson(const aki::Value& value)
{
    napi_value age, name;
    napi_get_named_property(aki::JSBind::GetScopedEnv(), value.GetHandle(), "age", &age);
    napi_get_named_property(aki::JSBind::GetScopedEnv(), value.GetHandle(), "name", &name);

    Person person;
    napi_get_value_int32(aki::JSBind::GetScopedEnv(), age, &person.age);

    size_t stringSize = 0;
    napi_get_value_string_utf8(aki::JSBind::GetScopedEnv(), name, nullptr, 0, &stringSize); // 获取字符串长度
    person.name.resize(stringSize + 1);
    // 根据长度传换成字符串
    napi_get_value_string_utf8(aki::JSBind::GetScopedEnv(), name, &person.name[0], stringSize + 1, &stringSize);
    AKI_LOG(INFO) << person.name << " : " << person.age;
}

static std::map<std::vector<int>, aki::ArrayBuffer> pushMapKeyArrayValueArrayBuffer(aki::Value value)
{
    auto map = value.AsMap<std::vector<int>, aki::ArrayBuffer>();
    for (auto& [key, value] : map) {
        printVector(key);
        printArrayBuffer(value);
    }
    return map;
}

static std::map<std::vector<int>, std::vector<int>> pushMapKeyArrayValueArray(aki::Value value)
{
    auto map = value.AsMap<std::vector<int>, std::vector<int>>();
    for (auto& [key, value] : map) {
        printVector(key);
        printVector(value);
    }
    return map;
}

static std::map<std::vector<int>, aki::Value> pushMapKeyArrayValueObject(aki::Value value)
{
    auto map = value.AsMap<std::vector<int>, aki::Value>();
    for (auto& [key, value] : map) {
        printVector(key);
        printPerson(value);
    }
    return map;
}

static std::map<aki::Value, aki::ArrayBuffer> pushMapKeyObjectValueArrayBuffer(aki::Value value)
{
    auto map = value.AsMap<aki::Value, aki::ArrayBuffer>();
    for (auto& [key, value] : map) {
        printPerson(key);
        printArrayBuffer(value);
    }
    return map;
}

static std::map<aki::Value, std::vector<int>> pushMapKeyObjectValueArray(aki::Value value)
{
    auto map = value.AsMap<aki::Value, std::vector<int>>();
    for (auto& [key, value] : map) {
        printPerson(key);
        printVector(value);
    }
    return map;
}

static std::map<aki::Value, aki::Value> pushMapKeyObjectValueObject(aki::Value value)
{
    auto map = value.AsMap<aki::Value, aki::Value>();
    for (auto& [key, value] : map) {
        printPerson(key);
        printPerson(value);
    }
    return map;
}

JSBIND_GLOBAL()
{
    JSBIND_FUNCTION(pushMapKeyArrayValueArrayBuffer, "pushMapKeyArrayValueArrayBuffer");
    JSBIND_FUNCTION(pushMapKeyArrayValueArray, "pushMapKeyArrayValueArray");
    JSBIND_FUNCTION(pushMapKeyArrayValueObject, "pushMapKeyArrayValueObject");
    JSBIND_FUNCTION(pushMapKeyObjectValueArrayBuffer, "pushMapKeyObjectValueArrayBuffer");
    JSBIND_FUNCTION(pushMapKeyObjectValueArray, "pushMapKeyObjectValueArray");
    JSBIND_FUNCTION(pushMapKeyObjectValueObject, "pushMapKeyObjectValueObject");
}

JSBIND_ADDON(entry)